#include <glf/core/inlines.h>

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define WIN32_EXTRA_LEAN
#include <windows.h>
#include <AccCtrl.h>
#include <Aclapi.h>
#undef Yield


#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
   DWORD dwType; // Must be 0x1000.
   LPCSTR szName; // Pointer to name (in user addr space).
   DWORD dwThreadID; // Thread ID (-1=caller thread).
   DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

namespace glf {

ThreadId GetCurrentThreadId() {
 	return ::GetCurrentThreadId();
}

bool allocTlsKey(TlsKey& key) {
 	key = TlsAlloc();
 	return key != TLS_OUT_OF_INDEXES;
}

bool freeTlsKey(TlsKey key) {
 	return TlsFree(key) != 0;
}

void* getTlsValue(TlsKey key) {
 	return TlsGetValue(key);
}

bool setTlsValue(TlsKey key, void* value) {
 	return TlsSetValue(key, value) != 0;
}

Thread::Features Thread::GetFeatures() {
	return (Thread::Features)(FEATURE_MAX_THREADS | 
							  FEATURE_MAIN_CAN_SLEEP | 
							  FEATURE_CAN_SLEEP | 
							  FEATURE_CAN_SUSPEND_RESUME);
}

#if GLF_AS_DLL

const int THREAD_PRIORITY_MIN = 0;
const int THREAD_PRIORITY_MAX = 0;
const int THREAD_PRIORITY_NORM = 0;

#else

const int Thread::PRIORITY_MIN = 0;
const int Thread::PRIORITY_MAX = 0;
const int Thread::PRIORITY_NORM = 0;

#endif

struct Thread::Impl : public thread::ImplBase {
	HANDLE mHandle;
	DWORD mId;
	bool mIsMain;

	Impl(Thread& thread) :
		ImplBase(thread),
		mHandle(0),
		mId(0),
		mIsMain(false) {
	}

	Impl(Thread& thread, bool isMain) :
		ImplBase(thread),
		mHandle(::GetCurrentThread()),
		mId(::GetCurrentThreadId()),
		mIsMain(isMain) {
	}

	~Impl () {
	}

	void Resume() {
		::ResumeThread(mHandle);
	}

	void Suspend() {
		::SuspendThread(mHandle);
	}

	void Join() {
		if(mHandle)
		{
			::WaitForSingleObject(mHandle, INFINITE);
			TerminateThread(mHandle, 0);
			mHandle = 0;
			mId = 0;
		}
	}

	static DWORD WINAPI RunWin32(void* ud) {
		Impl* impl = static_cast<Impl*>(ud);
		
		Thread::MyRun(impl->mThread);

		return 0;
	}

	void SetCoreAffinity(int flag) {
		DWORD error = SetThreadAffinityMask(mHandle, flag);
	}

	int GetCoreAffinity() const {
		DWORD_PTR procMask, sysMask;
		DWORD err = GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);
		
		return procMask;
	}

	bool Start(int priority, uint stackSize) {
		GLFi_ASSERT(mHandle == 0);

		DWORD dwCreationFlags = 0; // CREATE_SUSPENDED | CREATE_NEW_CONSOLE
		mHandle = ::CreateThread(0, stackSize, RunWin32, this, dwCreationFlags, &mId);

		if(mHandle == 0)
			return false;

		return true;
	}

	void SetName(const stringc& threadName) {
		THREADNAME_INFO info;
		info.dwType = 0x1000;
		info.szName = threadName.c_str();
		info.dwThreadID = mId;
		info.dwFlags = 0;
		const DWORD MS_VC_EXCEPTION=0x406D1388;
		__try
		{
			RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
		}
		__except(EXCEPTION_EXECUTE_HANDLER) {}

		mName = threadName;
	}

	bool IsCurrent() const {
		DWORD threadId = ::GetCurrentThreadId();
		return mId == threadId;
	}

	bool IsMain() const {
		return mIsMain;
	}

	static void Yield() {
		
	}

	static void Sleep(uint ms) {
		::SleepEx(ms, true);
	}
};

} //namespace glf

